namespace OFX.Aggregator.Infrastructure
{
	#region #using Directives

	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Diagnostics;
	using System.Runtime.Serialization;
	using System.Security.Permissions;

	#endregion

	[Serializable]
	public sealed class StatementSource : ISerializable,
	                                      INotifyPropertyChanged
	{
		private readonly string templateId;
		private IIocContainerAbstraction container;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)] private string displayName;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)] private DateTime? lastUpdate;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)] private DateTime lowerCap;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)] private IDictionary<string, string> parametersDictionary;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)] private StatementSourceTemplate template;

		public StatementSource(string templateId)
		{
			this.templateId = templateId;
			this.displayName = Template.DisplayName;
			this.lowerCap = DateTime.Now - new TimeSpan(30, 0, 0, 0);
		}

		private IIocContainerAbstraction Container
		{
			get
			{
				if (this.container == null)
				{
					this.container = SingletonContainer.Resolve<IIocContainerAbstraction>();
				}
				return this.container;
			}
		}

		public StatementSourceTemplate Template
		{
			get
			{
				if (this.template == null)
				{
					this.template = Container.Resolve<StatementSourceTemplate>(this.templateId);
				}

				return this.template;
			}
		}

		public IStatementRetrieval Retrieval
		{
			[DebuggerStepThrough]
			get { return Template.Retrieval; }
		}

		public IStatementInterpreter Interpreter
		{
			[DebuggerStepThrough]
			get { return Template.Interpreter; }
		}

		public IStatementProcessor Postprocessor
		{
			[DebuggerStepThrough]
			get { return Template.Postprocessor; }
		}

		public IStatementSerializer Serializer
		{
			[DebuggerStepThrough]
			get { return Template.Serializer; }
		}

		public IStatementOfxConverter OfxConverter
		{
			[DebuggerStepThrough]
			get { return Template.OfxConverter; }
		}

		public string DisplayName
		{
			[DebuggerStepThrough]
			get { return this.displayName; }
			set
			{
				if (value != this.displayName)
				{
					this.displayName = value;
					FirePropertyChanged("DisplayName");
				}
			}
		}

		public DateTime? LastUpdate
		{
			[DebuggerStepThrough]
			get { return this.lastUpdate; }
			private set
			{
				if (value != this.lastUpdate)
				{
					this.lastUpdate = value;
					FirePropertyChanged("LastUpdate");
				}
			}
		}

		public DateTime LowerCap
		{
			[DebuggerStepThrough]
			get { return this.lowerCap; }
			[DebuggerStepThrough]
			set
			{
				if (value != this.lowerCap)
				{
					this.lowerCap = value;
					FirePropertyChanged("LowerCap");
				}
			}
		}

		public IDictionary<string, string> Parameters
		{
			[DebuggerStepThrough]
			get { return this.parametersDictionary; }
		}

		public TimeSpan TransactionListingLag
		{
			[DebuggerStepThrough]
			get { return Template.TransactionListingLag; }
		}

		#region INotifyPropertyChanged Members

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion

		public void SetParameters(IDictionary<string, string> value)
		{
			this.parametersDictionary = value;

			FirePropertyChanged("Parameters");
		}

		[DebuggerStepThrough]
		public void MarkUpdated()
		{
			LastUpdate = DateTime.Now;
			LowerCap = this.lastUpdate.Value - TransactionListingLag;
		}

		private void FirePropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}

		#region Implementation of ISerializable

		private StatementSource(SerializationInfo info, StreamingContext context)
		{
			int revision = info.GetInt32("revision");

			Debug.Assert(revision <= 3);

			this.templateId = info.GetString("templateId");
			this.parametersDictionary = (IDictionary<string, string>) info.GetValue("parameters", typeof(Dictionary<string, string>));
			this.lastUpdate = (DateTime?) info.GetValue("lastUpdate", typeof(DateTime?));
			if (revision < 3)
			{
				this.lowerCap = DateTime.Now - new TimeSpan(30, 0, 0, 0);
			}

			if (revision >= 2)
			{
				this.displayName = info.GetString("displayName");

				if (revision >= 3)
				{
					this.lowerCap = info.GetDateTime("lowerCap");
				}
			}
		}

		[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
		public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			info.AddValue("revision", 3);
			info.AddValue("templateId", this.templateId);
			info.AddValue("displayName", this.displayName);
			info.AddValue("parameters", this.parametersDictionary, typeof(IDictionary<string, string>));
			info.AddValue("lastUpdate", this.lastUpdate);
			info.AddValue("lowerCap", this.lowerCap);
		}

		#endregion
	}
}